#include "codeobj.hpp"

#include <iostream>
#include <fstream>
#include <string>
using namespace std;

int CodeObject::write_to_file(string& filename, CodeObject& co) {
    ofstream file = ofstream(filename, ios::out | ios::binary);
    int magic_number = 0xa0df303;

    if (!file.good()) {
        cerr << "stream buf state is bad" << endl;
        return -1;
    }

    file.write(reinterpret_cast<const char*>(&magic_number), 4);
    // time
    file.write(reinterpret_cast<const char*>(&magic_number), 4);

    co.save_to_file(file);

    file.close();
    return 0;
}

int CodeObject::save_to_file(ofstream& file) {
    file << 'c';
    save_const_value(_arg_count, file);
    save_const_value(_nlocals, file);
    save_const_value(_stack_size, file);
    save_const_value(_flags, file);

    save_code_to_file(file);
    save_vector(file, _consts);
    save_vector(file, _names);
    save_vector(file, _var_names);
    save_vector(file, _frees);
    save_vector(file, _cells);
    save_string(file, _file_name);
    save_string(file, _module_name);
    save_const_value(_begin_lineno, file);
    save_string(file, _lnotab);

    return 0;
}

template<typename T>
int CodeObject::save_const_value(T t, ofstream& file) {
    file.write(reinterpret_cast<const char*>(&t), sizeof(T));
    return 0;
}

int CodeObject::save_string(ofstream& file, string& s) {
    file << 's';
    save_const_value((int)s.size(), file);
    file << s;
    return 0;
}

int CodeObject::save_code_to_file(ofstream& file) {
    file << 's';
    save_const_value((int)_insts.size(), file);
    for (auto it = _insts.begin(); it != _insts.end(); it++) {
        file << *it;
    }
    return 0;
}

int CodeObject::save_vector(ofstream& file, vector<Constant>& v) {
    file << '(';
    save_const_value((int)v.size(), file);

    for (auto it = v.begin(); it != v.end(); it++) {
        switch(it->const_type) {
        case ValueType::VT_INT:
            file << 'i';
            save_const_value((int)((it->value).int_value), file);
            break;
        case ValueType::VT_STRING:
            file << "s";
            save_const_value((int)((it->value).str_value->length()), file);
            file << *it->value.str_value;
            break;
        case ValueType::VT_UNIT:
            file << "N";
            break;
        case ValueType::VT_TRUE:
            break;
        case ValueType::VT_FALSE:
            break;
        case ValueType::VT_CODE_OBJECT:
            (it->value).co_value->save_to_file(file);
            break;
        default:
            cerr << "Unknown Constant type: " << it->const_type << endl;
        }
    }

    return 0;
}

int CodeObject::save_vector(ofstream& file, vector<string>& v) {
    file << '(';
    save_const_value((int)v.size(), file);

    for (auto it = v.begin(); it != v.end(); it++) {
        file << 's';
        save_const_value((int)(*it).length(), file);
        file << *it;
    }

    return 0;
}

